package com.xinxin.blockchain.service.impl;

import com.aliyuncs.exceptions.ClientException;
import com.xinxin.base.entity.Claim;
import com.xinxin.base.enums.CountryCodeEnum;
import com.xinxin.base.enums.TokenTypeEnum;
import com.xinxin.base.exception.BusinessException;
import com.xinxin.base.service.IRedisService;
import com.xinxin.base.service.ITokenService;
import com.xinxin.base.util.CommonUtil;
import com.xinxin.base.util.JsonUtil;
import com.xinxin.blockchain.constant.ErrorCode;
import com.xinxin.blockchain.constant.RedisKeyConstant;
import com.xinxin.blockchain.enums.MsgCodeTypeEnum;
import com.xinxin.blockchain.model.Member;
import com.xinxin.blockchain.service.ICommonService;
import com.xinxin.blockchain.service.IMemberService;
import com.xinxin.blockchain.util.legacy.DayuSmsUtil;
import com.xinxin.blockchain.vo.CountryCodeVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ys
 * @date 2021/5/20
 */
@Slf4j
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class CommonServiceImpl implements ICommonService {

    @Autowired
    private IRedisService redisService;
    @Autowired
    private ITokenService tokenService;

    @Autowired
    private IMemberService memberService;

    @Override
    @SuppressWarnings("unchecked")
    public List<CountryCodeVO> countryCode() {
        return redisService.orElseGet(RedisKeyConstant.COUNTRY_CODE, List.class, () ->
                Arrays.stream(CountryCodeEnum.values())
                        .map(country -> {
                                    int code = country.getCode();
                                    String name = country.getName(CountryCodeEnum.SIMPLE_CHINESE);
                                    return new CountryCodeVO(code, name);
                                }
                        ).sorted(Comparator.comparingInt(CountryCodeVO::getCode))
                        .collect(Collectors.toList())
        );
    }

    @Override
    public void sendSmsCode(Integer countryCode, String mobile, MsgCodeTypeEnum type) {
        String fullMobile = countryCode + "-" + mobile;
        checkCodeType(fullMobile, type);

        String key = type.getRedisKey() + fullMobile;
        String code = CommonUtil.generateCode(100000, 999999);
        boolean setnx = redisService.setnx(key, code, DayuSmsUtil.EFFECTIVE, TimeUnit.SECONDS);
        if (!setnx) {
            Long expire = redisService.getExpire(key);
            if (DayuSmsUtil.EFFECTIVE - expire < DayuSmsUtil.PERIOD) {
                throw new BusinessException(ErrorCode.TOO_MANY_REQUESTS);
            } else {
                redisService.setObject(key, code, DayuSmsUtil.EFFECTIVE, TimeUnit.SECONDS);
            }
        }

        String template;
        String sendMobile;
        if (CountryCodeEnum.CHINA.getCode() == countryCode) {
            template = DayuSmsUtil.TEMPLATE_CODE_HOME_EN;
            sendMobile = mobile;
        } else {
            template = DayuSmsUtil.TEMPLATE_CODE_INTERNATIONAL_EN;
            sendMobile = fullMobile.replace("-", "");
        }

        log.info("send msg code, type: {}, template: {}, mobile: {}", type.name(), template, sendMobile);
        try {
            String data = DayuSmsUtil.sendCode(DayuSmsUtil.SIGN, template, sendMobile, code);
            log.info("sms send result: {}", data);

            Map map = JsonUtil.readValue(data, Map.class);
            if (!"OK".equals(map.get("Code"))) {
                throw new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR);
            }
        } catch (ClientException e) {
            log.error("send sms error: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_SERVER_ERROR);
        }
    }

    private void checkCodeType(String fullMobile, MsgCodeTypeEnum type) {
        if (type == MsgCodeTypeEnum.SIGN_UP_IN) {
            return;
        }

        Member member = memberService.getMemberByMobile(fullMobile);
        if (member == null) {
            throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
        }
    }

    @Override
    public String refreshToken(String refreshToken) {
        Claim claim = tokenService.validClaim(TokenTypeEnum.REFRESH, refreshToken);
        Long accountId = claim.getAccountId();

        String secret = memberService.getAccountSecret(accountId);
        if (StringUtils.isBlank(secret)) {
            throw new BusinessException(ErrorCode.UNAUTHORIZED);
        }

        String signature = tokenService.signature(claim.getPayload(), secret);
        if (!signature.equals(claim.getSignature())) {
            throw new BusinessException(ErrorCode.UNAUTHORIZED);
        }

        return tokenService.generateToken(TokenTypeEnum.AUTHORIZATION, accountId, secret);
    }
}
